﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Collections.Generic;

namespace SuperDeepZoom
{
    /// <summary>
    /// Handles sequential display of images in the MSI using the ImageArranger.
    /// </summary>
    public class Slideshow : IDisposable
    {
        MultiScaleImage msi;
        ImageArranger arranger;

        DispatcherTimer slideshowTimer;

        int currentImageIndex = 0;

        bool running = false;

        bool paused = false;

        List<MultiScaleSubImage> imagesToShow;

        public event EventHandler SlideShowRunStateChanged;

        internal Slideshow(MultiScaleImage msi, ImageArranger arranger)
        {
            this.msi = msi;
            this.arranger = arranger;

            arranger.ImagesRearranged += new EventHandler(arranger_ImagesRearranged);            
        }        
    
        //Slideshow allows a user to run sequentially through a series of images.
        //I needs to support filtering, so it actually runs through the images in the
        //filtered image list (arranger.ImagesToShow) and not the msi sub images directly

        public void StartSlideShow(TimeSpan delay, List<MultiScaleSubImage> imagesToShow)
        {
            this.imagesToShow = imagesToShow;
            paused = false;
            bool initialZoom = false;

            ////Move the image to the first image if needed
            ////Otherwise start from currently viewed image

            if (arranger.ViewedImage == null || !arranger.ViewedImage.HasValue)
            {
                //this flag will make the slide show
                //start on the first image as no image 
                //was being shown already
                initialZoom = true;
                currentImageIndex = 0;
            }
            else if(arranger.ViewedImage.HasValue)
            {
                int? currentFilteredImage = getFilteredImageIndex(msi.SubImages[arranger.ViewedImage.Value]);
                if (currentFilteredImage != null)
                {
                    currentImageIndex = currentFilteredImage.Value;
                }
                else
                {
                    initialZoom = true;
                    currentImageIndex = 0;
                }
            }

            arranger.ViewedImage = getImageRealIndex(imagesToShow[currentImageIndex]);

            slideshowTimer = new DispatcherTimer();
            slideshowTimer.Interval = delay;

            slideshowTimer.Tick += (s, e) =>
            {
                if (!paused)
                {
                    //evey time the timer fires, the system will go to the next
                    //image.
                    if (arranger.ViewedImage != null && arranger.ViewedImage.HasValue)
                    {
                        NextSlide();
                    }
                    else
                    {
                        //the user must have zoomed out, so the system will
                        //stop the slideshow automatically
                        StopSlideShow();
                    }
                }
            };

            slideshowTimer.Start();

            if (initialZoom)
            {
                //start on the first image as no image 
                //was being shown already
                arranger.DisplaySubImageCentered(arranger.ViewedImage.Value, true);                        
            }

            running = true;
            onRunStateChanged();            
        }

        public void StopSlideShow()
        {
            paused = false;
            if (slideshowTimer != null)
            {
                slideshowTimer.Stop();
                slideshowTimer = null;
            }
            imagesToShow = null;
            currentImageIndex = 0;            
            running = false;
            onRunStateChanged();
        }

        public void NextSlide()
        {
            subInit();
            //if there is no viewed image it needs to go to the first slide and zoom it
            if (arranger.ViewedImage != null && arranger.ViewedImage.HasValue)
            {
                int count = imagesToShow.Count;
                currentImageIndex++;

                //check that the slideshow has not gone off the end of the available images
                if (currentImageIndex >= count)
                {
                    currentImageIndex = 0;
                }                
            }
            else
            {
                currentImageIndex = 0;
            }

            arranger.ViewedImage = getImageRealIndex(imagesToShow[currentImageIndex]);

            if (slideshowTimer != null)
            {
                //ensure the delay for the next slide is normalised
                slideshowTimer.Stop();
                slideshowTimer.Start();
            }
            arranger.DisplaySubImageCentered(arranger.ViewedImage.Value, true);
        }

        public void PrevSlide()
        {
            subInit();
            int count = imagesToShow.Count;           

            if (arranger.ViewedImage != null && arranger.ViewedImage.HasValue)
            {
                currentImageIndex--;
                if (currentImageIndex < 0)
                {
                    currentImageIndex = count - 1;
                }              
            }
            else
            {
                currentImageIndex = count - 1;
            }

            arranger.ViewedImage = getImageRealIndex(imagesToShow[currentImageIndex]);

            if (slideshowTimer != null)
            {
                //ensure the delay for the next slide is normalised
                slideshowTimer.Stop();
                slideshowTimer.Start();
            }
            arranger.DisplaySubImageCentered(arranger.ViewedImage.Value, true);
        }

        public void Pause()
        {
            paused = true;
        }

        public void Resume()
        {
            paused = false;
        }

        void onRunStateChanged()
        {
            if (SlideShowRunStateChanged != null)
            {
                SlideShowRunStateChanged(this, EventArgs.Empty);
            }
        }


        void subInit()
        {
            if (imagesToShow == null || imagesToShow.Count == 0)
            {
                imagesToShow = arranger.ImagesToShow;
                currentImageIndex = 0;
            }
        }

        /// <summary>
        /// Converts the index of a filtered image list item to it's index in the real msi image list
        /// </summary>        
        int getImageRealIndex(MultiScaleSubImage image)
        {
            for (int i = 0; i < msi.SubImages.Count; i++)
            {
                if (msi.SubImages[i] == image)
                {
                    return i;
                }
            }

            //if it gets to here then this program has a problem :)
            throw new Exception("Filtered image list and MultiScaleSubImage list are out of sychronisation");
        }

        /// <summary>
        /// Convetrs a msi sub image item to its position in the filtered image list
        /// </summary>       
        int? getFilteredImageIndex(MultiScaleSubImage image)
        {
            for (int i = 0; i < imagesToShow.Count; i++)
            {
                if (imagesToShow[i] == image)
                {
                    return i;
                }
            }

            //The MSI sub image does not have to be in the filtered list.
            return null;
        }

        #region Properties

        public bool IsRunning
        {
            get
            {
                return running;
            }
        }

        #endregion

        #region EventHandlers

        void arranger_ImagesRearranged(object sender, EventArgs e)
        {            
            StopSlideShow();
        }

        #endregion


        #region IDisposable Members

        public void Dispose()
        {
            arranger.ImagesRearranged -= new EventHandler(arranger_ImagesRearranged);   
        }

        #endregion
    }
}
